using System;
using MathLib;

namespace UraniaLib
{
	/// <summary>
	/// Summary description for UraniaTime.
	/// </summary>
	public class UraniaTime
	{
		public UraniaTime()
		{
		}


		public  static DateTime CalcUTFromZT(DateTime dDate, int iZone)
		{
			if (iZone >= 0)
			{
				return dDate.Subtract(new TimeSpan(iZone, 0, 0));
			}
			else
			{
				return dDate.AddHours(Math.Abs(iZone));
			}
		}

		public static DateTime CalcZTFromUT(DateTime dDate, int iZone) 
		{
			if (iZone >= 0)
			{
				return dDate.AddHours(iZone);
			}
			else
			{
				return dDate.Subtract(new TimeSpan(Math.Abs(iZone), 0, 0));
			}
		}

		public static DateTime CalcLMTFromUT(DateTime dDate, double fLong) 
		{
			bool  bAdd = false;
			DateTime dLongDate;
		
			dLongDate = ConvLongTUraniaTime(fLong,ref bAdd);

			if (bAdd == true)
			{
				dDate = dDate.Add(dLongDate.TimeOfDay);
			}
			else
			{
				dDate = dDate.Subtract(dLongDate.TimeOfDay);
			}
	
			return dDate;
		}

		public static DateTime CalcLMTFromZT(DateTime dDate, double fLong, int iZone)
		{
			return CalcLMTFromUT(CalcUTFromZT(dDate, iZone), fLong);
		}

		public static DateTime CalcUTFromLMT(DateTime dDate, double fLong)
		{
			bool bAdd = false;
			DateTime dLongDate;
			dLongDate = ConvLongTUraniaTime(fLong, ref bAdd);

			if (bAdd == true)
			{
				dDate = dDate.Subtract(dLongDate.TimeOfDay);
			}
			else
			{
				dDate = dDate.Add(dLongDate.TimeOfDay);
			}
			return dDate;
		}

		public static DateTime CalcLSTFromUT(DateTime dDate, double fLong)
		{
			double fGST;
			double fLST;
			DateTime dLST;
			bool bAdd = false;
			double fTimeDiff;

			fGST = ConvTimeToDec(CalcGSTFromUT(dDate));
			fTimeDiff = ConvTimeToDec(ConvLongTUraniaTime(fLong, ref bAdd));
		
			if (bAdd == true)
			{
				fLST = fGST + fTimeDiff;
			}
			else
			{
				fLST = fGST - fTimeDiff;
			}
	
			while (fLST > 24)
			{
				fLST = fLST - 24;
			}

			while (fLST < 0)
			{
				fLST = fLST + 24;
			}
			dLST = ConvDecTUraniaTime(fLST);
			return dLST;
		}

		public static DateTime CalcLSTFromZT(DateTime dDate, double fLong, int iZone)
		{
			return CalcLSTFromUT(CalcUTFromZT(dDate, iZone), fLong);
		}

		public static DateTime CalcUTFromLST(DateTime dDate, DateTime dCalDate, double fLong)
		{
			double fUT;
			DateTime dUT;
			bool bAdd = false;
			double fTimeDiff;

			fUT = ConvTimeToDec(CalcUTFromGST(dDate, dCalDate));
			fTimeDiff = ConvTimeToDec(ConvLongTUraniaTime(fLong, ref bAdd));
			
			if (bAdd == true)
			{
				fUT = fUT - fTimeDiff;
			}
			else
			{
				fUT = fUT + fTimeDiff;
			}
			fUT = Trig.PutIn24Hour(fUT);
			dUT = ConvDecTUraniaTime(fUT);
			return dUT;
		}

		public static DateTime CalcUTFromGST(DateTime dGSTDate, DateTime dCalDate)
		{
			double fJD;
			double fS;
			double fT;
			double fT0;
			double fGST;
			double fUT;
			DateTime dUT;

			bool bPrevDay;
			bool bNextDay;

			bPrevDay = false;
			bNextDay = false;

			fJD = GetJulianDay(dCalDate.Date, 0);
			fS = fJD - 2451545.0;
			fT = fS / 36525.0;
			fT0 = 6.697374558 + (2400.051336 * fT) + (0.000025862 * fT * fT);

			fT0 = Trig.PutIn24Hour(fT0);

			fGST = ConvTimeToDec(dGSTDate);
			fGST = fGST - fT0;
		
			while (fGST > 24)
			{
				fGST = fGST - 24;
				bNextDay = true;
			}
			while (fGST < 0)
			{
				fGST = fGST + 24;
				bPrevDay = true;
			}

			fUT = fGST * 0.9972695663;
			// fUT = fGST
		
			dUT = dCalDate.Date;
			dUT = dUT.AddHours(fUT);
			fUT = dUT.Millisecond;
			
			if (bNextDay == true)
			{
				dUT = dUT.AddDays(1);
			}

			if (bPrevDay == true)
			{
				dUT = dUT.Subtract(new TimeSpan(1, 0, 0, 0, 0));
			}
			return dUT;
		}

		public static DateTime CalcUTFromGSTMod(DateTime dGSTDate , DateTime dCalDate)
		{
			double fJD;
			double fS;
			double fT;
			double fT0;
			double fGST;
			double fUT;
			DateTime dUT;

			bool bPrevDay;
			bool bNextDay;

			bPrevDay = false;
			bNextDay = false;

			fJD = GetJulianDay(dCalDate.Date, 0);
			fS = fJD - 2451545.0;
			fT = fS / 36525.0;
			fT0 = 6.697374558 + (2400.051336 * fT) + (0.000025862 * fT * fT);

			fT0 = Trig.PutIn24Hour(fT0);

			fGST = ConvTimeToDec(dGSTDate);
			fGST = fGST - fT0;
			while (fGST > 24)
			{
				fGST = fGST - 24;
				bNextDay = true;
			}
			while (fGST < 0)
			{
				fGST = fGST + 24;
				bPrevDay = true;
			}
			// fUT = fGST * 0.9972695663
			fUT = fGST;
			dUT = dCalDate.Date;
			dUT = dUT.AddHours(fUT);
			fUT = dUT.Millisecond;
			if (bNextDay == true)
			{
				dUT = dUT.AddDays(1);
			}

			if (bPrevDay == true)
			{
				dUT = dUT.Subtract(new TimeSpan(1, 0, 0, 0, 0));
			}
			return dUT;
		}


		public static DateTime CalcGSTFromUT(DateTime dDate)
		{
			double fJD;
			double fS;
			double fT;
			double fT0;
			DateTime dGST;
			double fUT;
			double fGST;

			fJD = GetJulianDay(dDate.Date, 0);
			fS = fJD - 2451545.0;
			fT = fS / 36525.0;
			fT0 = 6.697374558 + (2400.051336 * fT) + (0.000025862 * fT * fT);
			fT0 = Trig.PutIn24Hour(fT0);
			fUT = ConvTimeToDec(dDate);
			fUT = fUT * 1.002737909;
			fGST = fUT + fT0;
			while (fGST > 24)
			{
				fGST = fGST - 24;
			}
			dGST = ConvDecTUraniaTime(fGST);
			return dGST;
		}

		public static DateTime CalcGSTFromZT(DateTime dDate, int iZone)
		{
			return CalcGSTFromUT(CalcUTFromZT(dDate, iZone));
		}

		public static DateTime ConvLongTUraniaTime(double fLong, ref bool bAdd)
		{
			//double fHours;
			double fMinutes;
			//double fSeconds;
			DateTime dDate;
			//DateTime dTmpDate;

			fMinutes = fLong * 4;
			if (fMinutes < 0)
			{
				bAdd = false;
			}
			else
			{
				bAdd = true;
			}
			fMinutes = Math.Abs(fMinutes);

			dDate = new DateTime();
			dDate = dDate.AddMinutes(fMinutes);
			return dDate;
		}

		public static double GetJulianDay(DateTime dDate, int iZone)
		{
			double fJD;
			double iYear;
			double iMonth;
			double iDay;
			double iHour;
			double iMinute;
			double iSecond;
			double iGreg;
			double fA;
			double fB;
			double fC;
			double fD;
			double fFrac;

			dDate = CalcUTFromZT(dDate, iZone);

			iYear = dDate.Year;
			iMonth = dDate.Month;
			iDay = dDate.Day;
			iHour = dDate.Hour;
			iMinute = dDate.Minute;
			iSecond = dDate.Second;
			fFrac = iDay + ((iHour + (iMinute / 60) + (iSecond / 60 / 60)) / 24);
			if (iYear < 1582)
			{
				iGreg = 0;
			}
			else
			{
				iGreg = 1;
			}
			if ((iMonth == 1) || (iMonth == 2))
			{
				iYear = iYear - 1;
				iMonth = iMonth + 12;
			}

			fA = (long)Math.Floor(iYear / 100);
			fB = (2 - fA + (long)Math.Floor(fA / 4)) * iGreg;
			if (iYear < 0)
			{
				fC = (int)Math.Floor((365.25 * iYear) - 0.75);
			}
			else
			{
				fC = (int)Math.Floor(365.25 * iYear);
			}
			fD = (int)Math.Floor(30.6001 * (iMonth + 1));
			fJD = fB + fC + fD + 1720994.5;
			fJD = fJD + fFrac;
			return fJD;
		}

		public static DateTime getDateFromJD(double fJD, int iZone)
		{
			DateTime dDate;
			int iYear;
			int fMonth;
			int iDay;
			int iHour;
			int iMinute;
			int iSecond;
			double fFrac;
			double fFracDay;
			int fI;
			int fA;
			int fB;
			int fC;
			int fD;
			int fE;
			int fG;

			fJD = fJD + 0.5;
			fI = (int)Math.Floor(fJD);
			fFrac = fJD - fI;

			if (fI > 2299160)
			{
				fA = (int)Math.Floor((fI - 1867216.25) / 36524.25);
				fB = fI + 1 + fA - (int)Math.Floor((double)(fA / 4));
			}
			else
			{
				fA = 0;
				fB = fI;
			}
			fC = fB + 1524;
			fD = (int)Math.Floor((fC - 122.1) / 365.25);
			fE = (int)Math.Floor(365.25 * fD);
			fG = (int)Math.Floor((fC - fE) / 30.6001);
			fFracDay = fC - fE + fFrac - (long)Math.Floor((double)(30.6001 * fG));
			iDay = (int)Math.Floor(fFracDay);
			fFracDay = (fFracDay - iDay) * 24;
			iHour = (int)Math.Floor(fFracDay);
			fFracDay = (fFracDay - iHour) * 60;
			iMinute = (int)Math.Floor(fFracDay);
			fFracDay = (fFracDay - iMinute) * 60;
			iSecond = (int)Math.Floor(fFracDay);

			if (fG < 13.5)
			{
				fMonth = fG - 1;
			}
			else
			{
				fMonth = fG - 13;
			}

			if (fMonth > 2.5)
			{
				iYear = (int)Math.Floor((double)(fD - 4716.0));
			}
			else
			{
				iYear = (int)Math.Floor((double)(fD - 4715.0));
			}


			dDate = new DateTime(iYear, (int)Math.Floor((double)fMonth), iDay, iHour, iMinute, iSecond);
			dDate = CalcZTFromUT(dDate, iZone);
			return dDate;
		}

		public static double ConvTimeToDec(DateTime dDate)
		{
			double fHour;

			fHour = dDate.Hour + (dDate.Minute / 60.0) + (dDate.Second / 60.0 / 60.0) + (dDate.Millisecond / 60.0 / 60.0 / 1000.0);
			return fHour;
		}

		public static DateTime ConvDecTUraniaTime(double fTime)
		{
			DateTime dDate;

			dDate = new DateTime();
			dDate = dDate.AddHours(fTime);
			return (dDate);
		}

		public static long GetDaysBetween(DateTime dDate, DateTime dSecDate)
		{
			double fDays;
			double fJD1;
			double fJD2;

			fJD1 = GetJulianDay(dDate, 0);
			fJD2 = GetJulianDay(dSecDate, 0);
			fDays = (fJD1 - fJD2);

			if (fDays >= 0)
			{
				fDays = (long)Math.Floor(fDays);
			}
			else
			{
				fDays = (long)Math.Floor(Math.Abs(fDays)) * -1;
			}
			return (long)Math.Floor(fDays);
		}

		public static int DaysInMonth(int iMonth)
		{
			if (iMonth == 1)
			{
				return 31;
			}
			else if (iMonth == 2)
			{
				return 28;
			} 
			else if (iMonth == 3)
			{
				return 31;
			} 			
			else if (iMonth == 4)
			{
				return 30;
			} 			
			else if (iMonth == 5)
			{
				return 31;
			} 			
			else if (iMonth == 6)
			{
				return 30;
			} 			
			else if (iMonth == 7)
			{
				return 31;
			} 
			else if (iMonth == 8)
			{
				return 31;
			} 
			else if (iMonth == 9)
			{
				return 30;
			} 
			else if (iMonth == 10)
			{
				return 31;
			} 
			else if (iMonth == 11)
			{
				return 30;
			} 
			else if (iMonth == 12)
			{
				return 31;
			} 
			else
			{
				return 0;
			}
		}

		public static long GetDayOfYear(DateTime dDate)
		{
			DateTime dBeginDate = new DateTime(dDate.Year, 1, 1);
			long iDay = 0;

			iDay = GetDaysBetween(dBeginDate, dDate) + 1;
			return iDay;
		}

		public static int GetDayOfWeek(DateTime dDate)
		{
			double dJD = 0.0;
			int iDOW =  -1;

			dJD = GetJulianDay(dDate, 0);
			dJD += 1.5;
			iDOW = ((int)Math.Round(dJD) % 7);
				
			return iDOW;
		}


		public static bool IsLeapYear(int iYear, int iCalendar)
			/* shCalendar JULIAN for julian, GREGORIAN for gregorian */
		{

			if (iCalendar == 0)
			{
				if ((iYear % 4) == 0)
				{
					return true;
				}
				else
				{
					return false;
				}
			}

			else
			{
				if (((iYear % 4) == 0) && (((iYear % 100) != 0) || ((iYear % 400) == 0)))
				{
					return true;
				}
				else
				{
					return false;
				}
			}
		}

		public static string DayName(int iDay)
		{
			string sDay;
			sDay = "";
			if (iDay == 0)
			{
				sDay = "Sunday";
			}
			else if (iDay ==1)
			{
				sDay = "Monday";
			}
			else if (iDay ==2)
			{
				sDay = "Tuesday";
			}
			else if (iDay ==3)
			{
				sDay = "Wednesday";
			}
			else if (iDay ==4)
			{
				sDay = "Thursday";
			}
			else if (iDay ==5)
			{
				sDay = "Friday";
			}
			else if (iDay ==6)
			{
				sDay = "Saturday";
			}
			return sDay;
		}

		public static string MonthName(int iMonth, int iFormat) 
		{
			string sMonth;

			sMonth = "";
			if (iMonth == 1)
			{
				sMonth = "January";
			}
			else if (iMonth == 2)
			{
				sMonth = "Febuary";
			}			
			else if (iMonth == 3)
			{
				sMonth = "March";
			}
			else if (iMonth == 4)
			{
				sMonth = "April";
			}
			else if (iMonth == 5)
			{
				sMonth = "May";
			}
			else if (iMonth == 6)
			{
				sMonth = "June";
			}
			else if (iMonth == 7)
			{
				sMonth = "July";
			}
			else if (iMonth == 8)
			{
				sMonth = "August";
			}
			else if (iMonth == 9)
			{
				sMonth = "September";
			}
			else if (iMonth == 10)
			{
				sMonth = "October";
			}
			else if (iMonth == 11)
			{
				sMonth = "November";
			}
			else if (iMonth == 12)
			{
				sMonth = "December";
			}
			
			if (iFormat == 1)
			{
				sMonth = sMonth.Substring(0,3);
			}
			else if (iFormat == 2)
			{
				sMonth = sMonth.Substring(0,1);
			}
			return sMonth;
		}
	}
}
